module Pos.Core.Common.SharedSeed
       ( SharedSeed (..)
       ) where

import           Universum

import qualified Data.Aeson as Aeson (FromJSON (..), ToJSON (..))
import qualified Data.ByteString as BS (pack, zipWith)
import qualified Data.ByteString.Char8 as BSC (pack)
import           Data.SafeCopy (base, deriveSafeCopySimple)
import qualified Data.Semigroup (Semigroup (..))
import qualified Formatting.Buildable as Buildable
import           Serokell.Util.Base16 (base16F, formatBase16)
import qualified Serokell.Util.Base16 as B16
import           Serokell.Util.Base64 (JsonByteString (..))
import           Text.JSON.Canonical (FromJSON (..), JSValue (..),
                     ReportSchemaErrors, ToJSON (..))

import           Pos.Binary.Class (Cons (..), Field (..), deriveSimpleBi)
import           Pos.Core.Constants (sharedSeedLength)
import           Pos.Util.Json.Canonical (formatJSString)
import           Pos.Util.Json.Parse (tryParseString)

-- | This is a shared seed used for follow-the-satoshi. This seed is
-- randomly generated by each party and eventually they agree on the
-- same value.
newtype SharedSeed = SharedSeed
    { getSharedSeed :: ByteString
    } deriving (Show, Eq, Ord, Generic, NFData, Typeable)

instance Buildable SharedSeed where
    build = formatBase16 . getSharedSeed

instance Semigroup SharedSeed where
    (<>) (SharedSeed a) (SharedSeed b) =
        SharedSeed $ BS.pack (BS.zipWith xor a b) -- fast due to rewrite rules

instance Monoid SharedSeed where
    mempty = SharedSeed $ BSC.pack $ replicate sharedSeedLength '\NUL'
    mappend = (Data.Semigroup.<>)
    mconcat = foldl' mappend mempty

instance Monad m => ToJSON m SharedSeed where
    toJSON (SharedSeed seed) = pure . JSString . formatJSString base16F $ seed

instance ReportSchemaErrors m => FromJSON m SharedSeed where
    fromJSON = fmap SharedSeed . tryParseString B16.decode

instance Aeson.ToJSON SharedSeed where
    toJSON = Aeson.toJSON . JsonByteString . getSharedSeed

instance Aeson.FromJSON SharedSeed where
    parseJSON v = SharedSeed . getJsonByteString <$> Aeson.parseJSON v

deriveSimpleBi ''SharedSeed [
    Cons 'SharedSeed [
        Field [| getSharedSeed :: ByteString |]
    ]]

deriveSafeCopySimple 0 'base ''SharedSeed
